﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using QMERP.Domain.Dtos;
using QMERP.Domain.Entities;
using QMERP.Domain.Enums;
using QMERP.Infrastructure.CustomException;
using QMERP.Infrastructure.EFCore;
using QMERP.Infrastructure.EFCore.Extensions;
using QMERP.Infrastructure.Extensions;
using QMERP.Infrastructure.Security;
using System.Data;

namespace QMERP.Application.Services
{
    public class InventoryOrderService : IInventoryOrderService
    {
        private readonly IMapper _mapper;
        private readonly QMERPDbContext _db;
        private readonly ISerialNumberService _serialNumberSvc;
        private readonly ICurrentUser _currentUser;
        private readonly IEnterStockOrderService _enterStockOrderService;
        private readonly ILeaveStockOrderService _leaveStockOrderService;
        public InventoryOrderService(IMapper mapper, QMERPDbContext db, ISerialNumberService serialNumberSvc, ICurrentUser currentUser, IEnterStockOrderService enterStockOrderService, ILeaveStockOrderService leaveStockOrderService)
        {
            _mapper = mapper;
            _db = db;
            _serialNumberSvc = serialNumberSvc;
            _currentUser = currentUser;
            _enterStockOrderService = enterStockOrderService;
            _leaveStockOrderService = leaveStockOrderService;
        }
        /// <summary>
        /// 库存盘点单窗体
        /// </summary>
        /// <returns></returns>
        public async Task<PageResponse<InventoryOrderResponse>> Query(PageRequest<InventoryOrderRequest> request)
        {
            var response = new PageResponse<InventoryOrderResponse>();
            var query = Where(request);
            var list = await query.Paging(request.PageIndex, request.PageSize).ToListAsync();
            response.TotalCount = await query.CountAsync();
            response.PageIndex = request.PageIndex;
            response.PageSize = request.PageSize;
            response.List = _mapper.Map<List<InventoryOrderResponse>>(list);
            return response;
        }
        /// <summary>
        /// 获取id的库存盘点单详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<InventoryOrderResponse> Detail(long id)
        {
            var model = await _db.InventoryOrder
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.InventoryOrderDetail_LensDetails)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Warehouse)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Lens).AsNoTracking().FindByIdAsync(id);
            var response = _mapper.Map<InventoryOrderResponse>(model);
            return response;
        }
        /// <summary>
        /// 库存盘点单保存
        /// </summary>
        /// <param name="request"></param>
        /// <param name="isCheck"></param>
        /// <returns></returns>
        /// <exception cref="ApiException"></exception>
        public async Task<InventoryOrderResponse> Save(InventoryOrderSaveRequest request, bool isCheck = false)
        {
            if (request.InventoryOrderDetails.Count <= 0)
                throw new ApiException("请填写盘点镜种信息!");

            for (int i = 0; i < request.InventoryOrderDetails.Count; i++)
            {
                string col = $"第{i + 1}行:";
                var detail = request.InventoryOrderDetails[i];
                if (detail.LensDetails.Count <= 0)
                    throw new ApiException($"{col}请导入XY表!");

                if (detail.LensDetails.Sum(s => s.Qty) <= 0)
                    throw new ApiException($"{col}XY表总片数为0,请检查!");

                var any = await (from a in _db.InventoryOrder
                                 join b in _db.InventoryOrderDetail on a.Id equals b.InventoryOrderId
                                 where a.Status != InventoryOrderStatusEnum.Finished.ToString() && a.Status != InventoryOrderStatusEnum.Canceled.ToString() && b.WarehouseId == detail.WarehouseId && detail.LensId == b.LensId && a.Id != request.Id
                                 select a).AnyAsync();
                if (any)
                    throw new ApiException($"{col}镜种存在未完成的盘点数据!");

                var stock = await _db.Stock.GetByWarehouseLens(detail.WarehouseId, detail.LensId);

                if (!detail.LensDetails.InRange(_mapper.Map<LensResponse>(stock.Lens)))
                    throw new ApiException($"{col}XY表度数超出镜种范围!");

                detail.LensDetails = detail.LensDetails.Where(x => x.Qty > 0).ToList();
            }    

            InventoryOrder? model;
            if (request.Id == null)
            {
                string number = await _serialNumberSvc.GetSerialNumber(SerialNumberTypeEnum.InventoryOrder);
                model = _mapper.Map<InventoryOrder>(request);
                model.Number = number;
                model.Status = InventoryOrderStatusEnum.Pending.ToString();
                await _db.AddAsync(model);
            }
            else
            {
                model = await _db.InventoryOrder.Include(x => x.InventoryOrderDetails).FindByIdAsync(request.Id.Value);
                if (model == null)
                    throw new ApiException("库存盘点单不存在!");
                model.Remark = request.Remark;
                model.InventoryOrderDetails = _mapper.Map<List<InventoryOrderDetail>>(request.InventoryOrderDetails);
            }
            await _db.SaveChangesAsync();
            if (isCheck)
            {
                model = await _db.InventoryOrder
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.InventoryOrderDetail_LensDetails)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Warehouse)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Lens).FindByIdAsync(model.Id);
                if (model != null)
                {
                    await Check(model);
                    await _db.SaveChangesAsync();
                } 
            }
            return _mapper.Map<InventoryOrderResponse>(model);
        }
        /// <summary>
        /// 库存盘点单审核
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task Check(long[] ids)
        {
            if (ids == null)
                throw new ApiException("id不能为空!");

            var orders = await _db.InventoryOrder
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.InventoryOrderDetail_LensDetails)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Warehouse)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Lens).Where(x => ids.Contains(x.Id)).ToListAsync();
            foreach (var order in orders)
            {
                if (order.Status != InventoryOrderStatusEnum.Pending.ToString())
                    throw new ApiException($"库存盘点单{order.Number}不能审核!");
                await Check(order);
            }
            await _db.SaveChangesAsync();
        }
        /// <summary>
        /// 库存盘点单弃审
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task UnCheck(long[] ids)
        {
            if (ids == null)
                throw new ApiException("id不能为空!");

            var orders = await _db.InventoryOrder.Where(x => ids.Contains(x.Id)).ToListAsync();
            foreach (var order in orders)
            {
                if (order.Status != InventoryOrderStatusEnum.Checked.ToString())
                    throw new ApiException($"库存盘点单{order.Number}不能弃审!");

                await UnCheckOrCancel(order);
                order.Status = InventoryOrderStatusEnum.Pending.ToString();
                order.CheckedBy = null;
                order.CheckedByName = null;
                order.CheckedOn = null;
            }
            await _db.SaveChangesAsync();
        }
        /// <summary>
        /// 库存盘点单完成
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task Finish(long[] ids)
        {
            if (ids == null)
                throw new ApiException("id不能为空!");

            var orders = await _db.InventoryOrder.Where(x => ids.Contains(x.Id)).ToListAsync();
            foreach (var order in orders)
            {
                if (order.Status != InventoryOrderStatusEnum.Checked.ToString())
                    throw new ApiException($"库存盘点单{order.Number}不能完成!");

                order.Status = InventoryOrderStatusEnum.Finished.ToString();
                order.FinishedBy = _currentUser.Id;
                order.FinishedByName = _currentUser.Name;
                order.FinishedOn = DateTime.Now;
            }
            await _db.SaveChangesAsync();
        }
        /// <summary>
        /// 库存盘点单取消
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task Cancel(long[] ids)
        {
            if (ids == null)
                throw new ApiException("id不能为空!");

            var orders = await _db.InventoryOrder.Where(x => ids.Contains(x.Id)).ToListAsync();
            foreach (var order in orders)
            {
                if (order.Status != InventoryOrderStatusEnum.Pending.ToString() && order.Status != InventoryOrderStatusEnum.Checked.ToString())
                    throw new ApiException($"库存盘点单{order.Number}不能取消!");

                await UnCheckOrCancel(order);
                order.Status = PurchaseOrderStatusEnum.Canceled.ToString();
            }
            await _db.SaveChangesAsync();
        }

        /// <summary>
        /// 获取库存盘点单主要信息列表
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task<List<InventoryOrderMainInfoResponse>> GetInventoryOrderMainInfos(PageRequest<InventoryOrderRequest> request)
        {
            var orders = await Where(request).ToListAsync();
            var details = orders.SelectMany(s => s.InventoryOrderDetails).ToList();
            return _mapper.Map<List<InventoryOrderMainInfoResponse>>(details);
        }
        private IQueryable<InventoryOrder> Where(PageRequest<InventoryOrderRequest> request)
        {
            IQueryable<InventoryOrder> query = _db.InventoryOrder
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.InventoryOrderDetail_LensDetails)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Warehouse)
                .Include(x => x.InventoryOrderDetails).ThenInclude(x => x.Lens).AsNoTracking();
            if (!request.Query.Number.IsNullOrWhiteSpace())
            {
                query = query.Where(x => x.Number.Contains(request.Query.Number!));
            }
            if (!request.Query.CreatedByName.IsNullOrWhiteSpace())
            {
                query = query.Where(x => x.CreatedByName.Contains(request.Query.CreatedByName!));
            }
            if (request.Query.CreatedOnStart != null)
            {
                query = query.Where(x => x.CreatedOn >= request.Query.CreatedOnStart.Value.ToStart());
            }
            if (request.Query.CreatedOnEnd != null)
            {
                query = query.Where(x => x.CreatedOn <= request.Query.CreatedOnEnd.Value.ToEnd());
            }
            if (!request.Query.Status.IsNullOrWhiteSpace())
            {
                query = query.Where(x => x.Status == request.Query.Status);
            }
            //if (request.Query.Status == InventoryOrderStatusEnum.Pending.ToString())
            //{
            //    query = query.OrderByDescending(x => x.CreatedOn);
            //}
            //else if (request.Query.Status == InventoryOrderStatusEnum.Checked.ToString())
            //{
            //    query = query.OrderByDescending(x => x.CheckedOn);
            //}
            //else if (request.Query.Status == InventoryOrderStatusEnum.Finished.ToString())
            //{
            //    query = query.OrderByDescending(x => x.FinishedOn);
            //}
            //else
            //{
            //    query = query.OrderByDescending(x => x.UpdatedOn);
            //}
            query = query.OrderByDescending(x => x.CreatedOn);
            return query;
        }
        private async Task Check(InventoryOrder order)
        {
            order.Status = InventoryOrderStatusEnum.Checked.ToString();
            order.CheckedBy = _currentUser.Id;
            order.CheckedByName = _currentUser.Name;
            order.CheckedOn = DateTime.Now;

            EnterStockOrderSaveRequest enterStockOrder = new()
            {
                BusinessOrderId = order.Id,
                BusinessOrderNumber = order.Number,
                BusinessOrderType = EnterStockBusinessOrderTypeEnum.InventoryOrder.ToString(),
                BusinessType = EnterStockBusinessTypeEnum.InventoryEnterStock.ToString(),
                Remark = order.Remark,
            };
            LeaveStockOrderSaveRequest leaveStockOrder = new()
            {
                BusinessOrderId = order.Id,
                BusinessOrderNumber = order.Number,
                BusinessOrderType = LeaveStockBusinessOrderTypeEnum.InventoryOrder.ToString(),
                BusinessType = LeaveStockBusinessTypeEnum.InventoryLeaveStock.ToString(),
                Remark = order.Remark,
            };
            foreach (var detail in order.InventoryOrderDetails)
            {
                EnterStockOrderDetailSaveRequest enterStockOrderDetail = new()
                {
                    BusinessOrderDetailId = detail.Id,
                    WarehouseId = detail.WarehouseId,
                    LensId = detail.LensId
                };
                LeaveStockOrderDetailSaveRequest leaveStockOrderDetail = new()
                {
                    BusinessOrderDetailId = detail.Id,
                    WarehouseId = detail.WarehouseId,
                    LensId = detail.LensId
                };
                var stock = await _db.Stock.Include(x => x.StockDetails).Include(x => x.Warehouse).Include(x => x.Lens).Where(x => x.LensId == detail.LensId && x.WarehouseId == detail.WarehouseId).FirstOrDefaultAsync();

                if (stock == null)
                    throw new ApiException($"此仓库{detail.Warehouse!.Code}的库存镜种{detail.Lens!.SalesLensCode}不存在!");

                foreach (var lensDetail in detail.InventoryOrderDetail_LensDetails)
                {
                    var stockDetail = stock.StockDetails.Where(x => x.DegreesType == lensDetail.DegreesType && x.SPH == lensDetail.SPH && x.CYL == lensDetail.CYL).FirstOrDefault();
                    if (stockDetail != null)
                    {
                        lensDetail.ProfitQty = (lensDetail.Qty - stockDetail.Qty) < 0 ? 0 : (lensDetail.Qty - stockDetail.Qty);
                        lensDetail.LossQty = (lensDetail.Qty - stockDetail.Qty) > 0 ? 0 : (stockDetail.Qty - lensDetail.Qty);
                        if (lensDetail.ProfitQty > 0)
                        {
                            LensDetailResponse enterStockOrderDetail_LensDetail = new()
                            {
                                DegreesType = lensDetail.DegreesType,
                                SPH = lensDetail.SPH,
                                CYL = lensDetail.CYL,
                                ADD = lensDetail.ADD,
                                Qty = lensDetail.ProfitQty
                            };
                            enterStockOrderDetail.LensDetails.Add(enterStockOrderDetail_LensDetail);
                        }
                        if (lensDetail.LossQty > 0)
                        {
                            LensDetailResponse leaveStockOrderDetail_LensDetail = new()
                            {
                                DegreesType = lensDetail.DegreesType,
                                SPH = lensDetail.SPH,
                                CYL = lensDetail.CYL,
                                ADD = lensDetail.ADD,
                                Qty = lensDetail.LossQty
                            };
                            leaveStockOrderDetail.LensDetails.Add(leaveStockOrderDetail_LensDetail);
                        }
                    }
                }
                enterStockOrder.EnterStockOrderDetails.Add(enterStockOrderDetail);
                leaveStockOrder.LeaveStockOrderDetails.Add(leaveStockOrderDetail);
            }
            if (enterStockOrder.EnterStockOrderDetails.SelectMany(s => s.LensDetails).Sum(s => s.Qty) > 0)
                await _enterStockOrderService.Save(enterStockOrder);
            if (leaveStockOrder.LeaveStockOrderDetails.SelectMany(s => s.LensDetails).Sum(s => s.Qty) > 0)
                await _leaveStockOrderService.Save(leaveStockOrder);
        }
        private async Task UnCheckOrCancel(InventoryOrder order)
        {
            var enterStockOrder = await _db.EnterStockOrder.Where(x => x.BusinessOrderId == order.Id && x.BusinessOrderType == EnterStockBusinessOrderTypeEnum.InventoryOrder.ToString() && x.Status != EnterStockOrderStatusEnum.Canceled.ToString()).FirstOrDefaultAsync();
            if (enterStockOrder != null)
            {
                if (enterStockOrder.Status == EnterStockOrderStatusEnum.Checked.ToString())
                    throw new ApiException($"库存盘点单号{order.Number}对应的入库单号{enterStockOrder.Number}已审核,不能弃审或取消!");
                await _enterStockOrderService.Cancel(new long[] { enterStockOrder.Id }, true);
            }
            var leaveStockOrder = await _db.LeaveStockOrder.Where(x => x.BusinessOrderId == order.Id && x.BusinessOrderType == LeaveStockBusinessOrderTypeEnum.InventoryOrder.ToString() && x.Status != LeaveStockOrderStatusEnum.Canceled.ToString()).FirstOrDefaultAsync();
            if (leaveStockOrder != null) 
            {
                if (leaveStockOrder.Status == EnterStockOrderStatusEnum.Checked.ToString())
                    throw new ApiException($"库存盘点单号{order.Number}对应的出库单号{leaveStockOrder.Number}已审核,不能弃审或取消!");
                await _leaveStockOrderService.Cancel(new long[] { leaveStockOrder.Id }, true);
            }
        }
    }
}
